import random
from Tkinter import *
import math

class Camera(object):
	def __init__(self,x=0,y=0,z=-200, roll=0, yaw=0, pitch=0):
		self.x = x
		self.y = y
		self.z = z
		self.roll = roll    #Roll: tourner autour de l'axe z (se toucher l'epaule avec les oreilles
		self.yaw = yaw      #Yaw: tourner autour de l'axe y (faire non avec la tete)
		self.pitch = pitch  #Pitch: tourner autour de l'axe x (regarder en haut, regarder en bas)
		self.oeil = Point(200,200,2000)
		self.milieu = Point(0,0,600)
		##L'oeil sert de reference pour la transposition des coordonnees sur l'ecran
		
	def centrerOeil(self, hauteur, largeur):
		self.oeil.x = largeur/2
		self.oeil.y = hauteur/2
		
	def rouler(self, angle):
		self.roll += angle
		##Axe des Z -> Ne devrait pas etre utile pour galax, on devrait pouvoir se contenter de pitch et yaw
		
	def pitcher(self, angle):
		
		x2 = self.x - self.milieu.x
		y2 = self.y - self.milieu.y
		z2 = self.z - self.milieu.z
		#		x = math.sin(angle)*z + math.cos(angle)*x
		#		z = math.cos(angle)*z - math.sin(angle)*x
		
		x = x2
		y = y2 * math.cos(-angle) - z2 * math.sin(-angle)
		z = y2 * math.sin(-angle) + z2 * math.cos(-angle)
		
		x2 = z * math.sin(0) + x * math.cos(0)
		y2 = y
		z2 = z * math.cos(0) - x * math.sin(0)
		
		x = y2 * math.sin(0) + x2 * math.cos(0)
		y = y2 * math.cos(0) - x2 * math.sin(0)
		z = z2;
		
		self.x = x+self.milieu.x
		self.y = y+self.milieu.y
		self.z = z+self.milieu.z
		self.pitch += angle
		##Axe des X
		
	def yawer(self, angle):
		#On applique ensuite les rotations sur les points pour avoir leur position relative
		#a la camera qui a peut-etre effectue une rotation
		
		x2 = self.x - self.milieu.x
		y2 = self.y - self.milieu.y
		z2 = self.z - self.milieu.z
		#		x = math.sin(angle)*z + math.cos(angle)*x
		#		z = math.cos(angle)*z - math.sin(angle)*x
		
		x = z2 * math.sin(-angle) + x2 * math.cos(-angle)
		y = y2
		z = z2 * math.cos(-angle) - x2 * math.sin(-angle)
		
		x2 = x
		y2 = y * math.cos(0) - z * math.sin(0)
		z2 = y * math.sin(0) + z * math.cos(0)
		
		x = y2 * math.sin(0) + x2 * math.cos(0)
		y = y2 * math.cos(0) - x2 * math.sin(0)
		z = z2;
		
		self.x = x+self.milieu.x
		self.y = y+self.milieu.y
		self.z = z+self.milieu.z
		self.yaw += angle
		##Axe des Y
		
	##Fonction pour bouger la camera
	def bougerX(self, mouvement):
		x = 0 * math.sin(-self.yaw) + mouvement * math.cos(-self.yaw)
		y = 0
		z = 0 * math.cos(-self.yaw) - mouvement * math.sin(-self.yaw)
		
		x2 = x
		y2 = y * math.cos(-self.pitch) - z * math.sin(-self.pitch)
		z2 = y * math.sin(-self.pitch) + z * math.cos(-self.pitch)
		
		x = y2 * math.sin(-self.roll) + x2 * math.cos(self.roll)
		y = y2 * math.cos(-self.roll) - x2 * math.sin(-self.roll)
		z = z2;
		
		self.x += x
		self.y += y
		self.z += z
		
	def bougerY(self, mouvement):
		x2 = 0
		y2 = mouvement * math.cos(-self.pitch) - 0 * math.sin(-self.pitch)
		z2 = mouvement * math.sin(-self.pitch) + 0 * math.cos(-self.pitch)
		
		x = z2 * math.sin(-self.yaw) + x2 * math.cos(-self.yaw)
		y = y2
		z = z2 * math.cos(-self.yaw) - x2 * math.sin(-self.yaw)

		x = y2 * math.sin(-self.roll) + x2 * math.cos(-self.roll)
		y = y2 * math.cos(-self.roll) - x2 * math.sin(-self.roll)
		z = z2;
		
		self.x += x
		self.y += y
		self.z += z
		
	def bougerZ(self, mouvement):
		x = mouvement * math.sin(-self.yaw) + 0 * math.cos(-self.yaw)
		y = 0
		z = mouvement * math.cos(-self.yaw) - 0 * math.sin(-self.yaw)
		
		x2 = x
		y2 = y * math.cos(-self.pitch) - z * math.sin(-self.pitch)
		z2 = y * math.sin(-self.pitch) + z * math.cos(-self.pitch)
		
		x = y2 * math.sin(-self.roll) + x2 * math.cos(-self.roll)
		y = y2 * math.cos(-self.roll) - x2 * math.sin(-self.roll)
		z = z2;
		
		self.x += x
		self.y += y
		self.z += z
		
	def zoom(self, zoom):
		self.oeil.z += zoom

	
class Objet3d(object):
	def __init__(self, x=0, y=0, z=0, polygones=[]):
		self.x = x
		self.y = y
		self.z = z
		self.polygones = polygones
		
class Polygone(object):
	def __init__(self, couleur, points=[]):
		self.points = points
		self.couleur = couleur
		
class Point(object):
	def __init__(self, x=0, y=0, z=0):
		self.x = x
		self.y = y
		self.z = z

		
def toEcranObjets(camera, objet, canvas):
	#toCameraObjets va creer une liste de polygones a dessiner
	liste = toCameraObjets(camera, objet)
	#on va ensuite transposer, polygone par polygone, les coordonnees des points
	#sur un plan 2d
	for poly in liste:
		coords = []
		for i in poly.points:
			if (i.z>=0):
				i.x = (camera.oeil.x + i.x*camera.oeil.z/(i.z+camera.oeil.z))
				i.y = (camera.oeil.y - i.y*camera.oeil.z/(i.z+camera.oeil.z))
				coords.append((i.x,i.y))
		canvas.create_polygon(coords, fill = poly.couleur)
		
	return liste
			
def toCameraObjets(camera, objet):
	liste=[]
	comptePoly=0
	for poly in objet.polygones:
		liste.append(Polygone(poly.couleur))
		for point in poly.points:
			#On applique ici une translation pour avoir les coordonnees des points dans un
			#systeme qui a la camera comme origine
			#La position des points est relative a la position de l'objet auquel
			#ils appartiennent
			x2 = point.x + objet.x - camera.x
			y2 = point.y + objet.y - camera.y
			z2 = point.z + objet.z - camera.z
			
			#On applique ensuite les rotations sur les points pour avoir leur position relative
			#a la camera qui a peut-etre effectue une rotation
			x = z2 * math.sin(camera.yaw) + x2 * math.cos(camera.yaw)
			y = y2
			z = z2 * math.cos(camera.yaw) - x2 * math.sin(camera.yaw)
			
			x2 = x
			y2 = y * math.cos(camera.pitch) - z * math.sin(camera.pitch)
			z2 = y * math.sin(camera.pitch) + z * math.cos(camera.pitch)
			
			x = y2 * math.sin(camera.roll) + x2 * math.cos(camera.roll)
			y = y2 * math.cos(camera.roll) - x2 * math.sin(camera.roll)
			z = z2;
			liste[comptePoly].points.append(Point(x,y,z))
		comptePoly+=1
			
	return liste

def toEcranCercles(camera, cercles, couleur):
	#les cercles sont une liste de points ou d'objets ayant des attributs x,y et z
	liste = toCameraCercles(camera, cercles)
	for cercle in liste:
	   cercle.x = (camera.oeil.x + cercle.x*camera.oeil.z/(cercle.z+camera.oeil.z))
	   cercle.y = (camera.oeil.y - cercle.y*camera.oeil.z/(cercle.z+camera.oeil.z))
	return liste
		
def toCameraCercles(camera, cercles):
	liste=[]
	for cercle in cercles:
		#pour chaque point, on effectue une translation pour avoir la position de la
		#camera comme origine
		x2 = cercle.x - camera.x
		y2 = cercle.y - camera.y
		z2 = cercle.z - camera.z
		
		#ensuite pour chaque point, on effectue une rotation pour avoir leur position
		#relative a la camera qui a peut-etre effectue une rotation
		x = z2 * math.sin(camera.yaw) + x2 * math.cos(camera.yaw)
		y = y2
		z = z2 * math.cos(camera.yaw) - x2 * math.sin(camera.yaw)
		
		x2 = x
		y2 = y * math.cos(camera.pitch) - cercle.z * math.sin(camera.pitch)
		z2 = y * math.sin(camera.pitch) + cercle.z * math.cos(camera.pitch)
		
		x = y2 * math.sin(camera.roll) + x2 * math.cos(camera.roll)
		y = y2 * math.cos(camera.roll) - x2 * math.sin(camera.roll)
		z = z2;
		liste.append(Point(x,y,z))
	return liste

def clicToMonde(camera, x,y,z):
	x2 = z*math.sin(-camera.yaw) + x*math.cos(-camera.yaw)
	y2 = y
	z2 = z*math.cos(-camera.yaw) - x*math.sin(-camera.yaw)
	
	x = x2
	y = y2*math.cos(-camera.pitch) - z2*math.sin(-camera.pitch)
	z = y2*math.sin(-camera.pitch) + z2*math.cos(-camera.pitch)
	
	x2 = y*math.sin(-camera.roll) + x*math.cos(-camera.roll)
	y2 = y*math.cos(-camera.roll) - x*math.sin(-camera.roll)
	z2 = z
	
	x = x2-camera.x
	y = y2-camera.y
	z = z2-camera.z
	
	return [x,y,z]

if __name__ == "__main__":
	#tests
	root = Tk()
	cam = Camera(0,0,0)
	points1 = [Point(0,0,20),Point(20,20,20),Point(20,0,20)]
	points2 = [Point(30,30,15),Point(30,60,15),Point(60,30,40),Point(60,60,40)]
	poly1 = Polygone("black", points1)
	poly2 = Polygone("blue",points2)
	objet = Objet3d(20,20,20,[poly1,poly2])
	
	cercles = [Point(20,20,20),Point(200,200,100),Point(150,150,200)]
	
	frame = Frame(root,bg='black')
	canvas = Canvas(frame, height = 800, width = 1000,bg='black')
	canvas.pack()
	frame.pack()
	toEcranObjets(cam,objet,canvas)
	toEcranCercles(cam,cercles,canvas, "white")
	root.mainloop()